package top.lilele.adminSystem.service;

import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Maps;
import io.minio.GetObjectResponse;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.multipart.MultipartFile;
import top.lilele.adminSystem.dao.FilePartDao;
import top.lilele.adminSystem.entity.AdminFileEntity;
import top.lilele.adminSystem.entity.AdminFilePartEntity;
import top.lilele.adminSystem.entity.FilePartEntity;
import top.lilele.adminSystem.mapper.AdminFileMapper;
import top.lilele.adminSystem.mapper.AdminFilePartMapper;
import top.lilele.adminSystem.mapper.FilePartMapper;
import top.lilele.adminSystem.utils.MinioUtil;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author lilele
 */
@Slf4j
@Service
public class AdminFilePartService {
    @Value("${minio.filePartBucketName}")
    private String filePartBucketName;

    @Value("${minio.bucketName}")
    private String bucketName;

    @Resource
    AdminFilePartMapper adminFilePartMapper;

    @Resource
    MinioUtil minioUtil;

    @Resource
    FilePartMapper filePartMapper;

    @Resource
    AdminFileMapper adminFileMapper;

    /**
     * 切片上传
     *
     * @param generateUploadId 批次id
     * @param chunkTotal       总片数
     * @param currentChunk     当前片数
     * @param md5              文件md5
     * @param multipartFile    文件
     */
    @SneakyThrows
    public void upload(String generateUploadId, Integer chunkTotal, Integer currentChunk, String md5, MultipartFile multipartFile, String finalMd5, String contentType, String originFileName) {
        // 构造切片信息实体
        FilePartEntity filePartEntity = new FilePartEntity();
        filePartEntity.setUploadId(generateUploadId);
        filePartEntity.setCurrentChunk(currentChunk);
        filePartEntity.setChunkTotal(chunkTotal);
        // 获取是否已经有当前的切片信息了
        AdminFilePartEntity info = adminFilePartMapper.selectOne(new LambdaQueryWrapper<AdminFilePartEntity>().eq(AdminFilePartEntity::getMd5, md5));
        if (info != null) {
            log.info("md5为--{}--已经有切片信息，直接默认秒传", md5);
            filePartEntity.setFileId(info.getId());
            filePartMapper.insert(filePartEntity);
            return;
        }
        // minio文件名
        String fileName = IdUtil.fastSimpleUUID();
        // 上传文件
        minioUtil.uploadStream(multipartFile.getInputStream(), fileName, filePartBucketName);
        // 构造文件实体
        AdminFilePartEntity adminFilePartEntity = new AdminFilePartEntity();
        adminFilePartEntity.setFileSize(multipartFile.getSize());
        adminFilePartEntity.setBucketName(filePartBucketName);
        adminFilePartEntity.setMd5(md5);
        adminFilePartEntity.setFileName(fileName);
        // 录入切片数据
        adminFilePartMapper.insert(adminFilePartEntity);
        // 录入批次上传数据
        filePartEntity.setFileId(adminFilePartEntity.getId());
        filePartMapper.insert(filePartEntity);
        //当在文件最后一个元素的时候，合并文件
        if (currentChunk + 1 == chunkTotal) {
            // 最终文件名
            Map map = mergePartAndUpload(generateUploadId);
            String finalFileName = map.get("fileName").toString();
            Integer finalFileSize = (Integer) map.get("fileSize");
            // 构造最终文件数据
            AdminFileEntity adminFileEntity = new AdminFileEntity();
            // 获取文件后缀名（按照.来切割，除了第一个. 全部都是后缀名）
            String[] split = originFileName.split("\\.");
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 1; i < split.length; i++) {
                stringBuilder.append(".");
                stringBuilder.append(split[i]);
            }
            adminFileEntity.setFileExtension(stringBuilder.toString());
            adminFileEntity.setFileName(finalFileName);
            adminFileEntity.setBucketName(bucketName);
            adminFileEntity.setContentType(contentType);
            adminFileEntity.setFileSize(Long.valueOf(finalFileSize));
            adminFileEntity.setMd5(finalMd5);
            adminFileEntity.setFileOriginName(originFileName);
            adminFileMapper.insert(adminFileEntity);
        }
    }

    @SneakyThrows
    public Map mergePartAndUpload(String generateUploadId) {
        // 获取当前批次上传的切片列表
        List<FilePartDao> list = filePartMapper.selectUploadedFilePart(generateUploadId);
        // 从minio下载的对象输入流集合
        List<InputStream> listInput = new ArrayList<>();
        list.stream().forEach(item -> {
            GetObjectResponse response;
            try {
                response = minioUtil.download(item.getFileName(), item.getBucketName());
                byte[] buf = new byte[1024];
                int len;
                try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
                    while ((len = response.read(buf)) != -1) {
                        os.write(buf, 0, len);
                    }
                    os.flush();
                    byte[] bytes = os.toByteArray();
                    ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
                    listInput.add(inputStream);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        // 定义内存输出流
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        // 定义缓冲字节数组
        byte[] array = new byte[1024];
        // 读取长度
        int len = 0;
        for (InputStream ips : listInput) {
            while ((len = ips.read(array)) > 0) {
                byteArrayOutputStream.write(array, 0, len);
            }
        }
        // 合并文件流
        InputStream inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        // 创建文件
        String fileName = IdUtil.fastSimpleUUID();
        log.info("最终生成的文件名 {},文件大小{}", fileName, inputStream.available());
        Map map = new HashMap<String, Object>(Maps.newHashMapWithExpectedSize(7));
        map.put("fileName", fileName);
        map.put("fileSize", inputStream.available());
        // 上传合并之后的字节流
        minioUtil.uploadStream(inputStream, fileName, bucketName);
        // 删除切片上传数据
        filePartMapper.delete(new LambdaQueryWrapper<FilePartEntity>().eq(FilePartEntity::getUploadId, generateUploadId));
        return map;
    }
}
